﻿using System;
using System.Configuration;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using WinwiseARDrone.API;
using WinwiseARDrone.TabletClient.Core;

namespace WinwiseARDrone.TabletClient.ViewModels
{
    /// <summary>
    /// ViewModel principal
    /// </summary>
    public class MainViewModel : BaseViewModel
    {
        #region Singleton

        public static MainViewModel Instance;

        #endregion

        #region Variables statiques

        private static bool DRONE_ACTIVATED = false;

        #endregion

        #region Prorpiétés

        /// <summary>
        /// Obtient ou définit l'atlitude du drone
        /// </summary>
        public int Altitude
        {
            get
            {
                return (this.m_iAltitude);
            }
            set
            {
                if (value < 0)
                    value = 0;

                if (value != this.m_iAltitude)
                {
                    this.m_iAltitude = value;
                    this.OnPropertyChanged("Altitude");
                    this.OnPropertyChanged("AltitudeString");
                }
            }
        }

        /// <summary>
        /// Obtient l'atlitude du drone
        /// </summary>
        public string AltitudeString
        {
            get
            {
                return (this.MilimetersToMeters(Convert.ToDouble(this.m_iAltitude)));
            }
        }

        /// <summary>
        /// Obtient ou définit le niveau de batterie du drone
        /// </summary>
        public int Batterie
        {
            get
            {
                return (this.m_iBaterie);
            }
            set
            {
                if (value != this.m_iBaterie)
                {
                    this.m_iBaterie = value;
                    this.OnPropertyChanged("Batterie");

                    // Refresh Clip Batterie

                    this.BatteryClip = new Rect(0, 0, 45 * (Convert.ToDouble(this.m_iBaterie) / 100), 20);
                }
            }
        }

        /// <summary>
        /// Obtient ou définit le clip pour la batterie
        /// </summary>
        public Rect BatteryClip
        {
            get
            {
                return (this.m_oClipBattery);
            }
            set
            {
                if (value != this.m_oClipBattery)
                {
                    this.m_oClipBattery = value;
                    this.OnPropertyChanged("BatteryClip");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la commande pour le reset Drone
        /// </summary>
        public CommandViewModel CommandReset
        {
            get
            {
                return (this.m_oReset);
            }
            set
            {
                if (value != this.m_oReset)
                {
                    this.m_oReset = value;
                    this.OnPropertyChanged("CommandReset");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la commande pour le démarage du drone
        /// </summary>
        public CommandViewModel CommandStart
        {
            get
            {
                return (this.m_oStart);
            }
            set
            {
                if (value != this.m_oStart)
                {
                    this.m_oStart = value;
                    this.OnPropertyChanged("CommandStart");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la commande pour l'arrêt du drone
        /// </summary>
        public CommandViewModel CommandStop
        {
            get
            {
                return (this.m_oStop);
            }
            set
            {
                if (value != this.m_oStop)
                {
                    this.m_oStop = value;
                    this.OnPropertyChanged("CommandStop");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la commande pour le switch caméra
        /// </summary>
        public CommandViewModel CommandSwitch
        {
            get
            {
                return (this.m_oSwitch);
            }
            set
            {
                if (value != this.m_oSwitch)
                {
                    this.m_oSwitch = value;
                    this.OnPropertyChanged("CommandSwitch");
                }
            }
        }

        /// <summary>
        /// Obtient si la double vue est utilisée
        /// </summary>
        public bool DoubleViewEnable
        {
            get
            {
                return (this.m_bVideoIntegrationEnable);
            }
        }

        /// <summary>
        /// Obtient ou définit si le drone est en mode Emergency
        /// </summary>
        public bool EmergencyMode
        {
            get
            {
                return (this.m_bEmergencyMode);
            }
            set
            {
                if (value != this.m_bEmergencyMode)
                {
                    this.m_bEmergencyMode = value;
                    this.OnPropertyChanged("EmergencyMode");

                    this.OnPropertyChanged("VisibilityDrone");
                    this.OnPropertyChanged("VisibilityEmergency");
                    this.OnPropertyChanged("VisibilityStart");
                }
            }
        }

        /// <summary>
        /// Obtient la hauteur de l'écrant en pixel
        /// </summary>
        public double ScreenHeight
        {
            get
            {
                return (SystemParameters.PrimaryScreenHeight);
            }
        }

        /// <summary>
        /// Obtient la largeur de l'écrant en pixel
        /// </summary>
        public double ScreenWidth
        {
            get
            {
                return (SystemParameters.PrimaryScreenWidth);
            }
        }

        /// <summary>
        /// Obtient la sensibilité pour l'altitude
        /// </summary>
        public double SensibilityAltitude
        {
            get
            {
                return (double.Parse(ConfigurationManager.AppSettings["StickSensibility.Altitude"], CultureInfo.GetCultureInfo("en-US")));
            }
        }

        /// <summary>
        /// Obtient la sensibilité pour le déplacement
        /// </summary>
        public double SensibilityMove
        {
            get
            {
                return (double.Parse(ConfigurationManager.AppSettings["StickSensibility.Move"], CultureInfo.GetCultureInfo("en-US")));
            }
        }

        /// <summary>
        /// Obtient la sensibilité pour la rotation
        /// </summary>
        public double SensibilityRotation
        {
            get
            {
                return (double.Parse(ConfigurationManager.AppSettings["StickSensibility.Rotation"], CultureInfo.GetCultureInfo("en-US")));
            }
        }

        /// <summary>
        /// Obtient ou définit la valeur du stick de déplacement en altitude
        /// </summary>
        public double StickAltitudeValue
        {
            get
            {
                return (DroneController.DroneInput.Gaz * -1);
            }
            set
            {
                if (value != DroneController.DroneInput.Gaz)
                {
                    DroneController.DroneInput.Gaz = Convert.ToSingle(value) * -1;
                    this.OnPropertyChanged("StickAltitudeValue");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la valeur du stick de déplacement en rotation
        /// </summary>
        public double StickRotateValue
        {
            get
            {
                return (DroneController.DroneInput.Yaw * -1);
            }
            set
            {
                if (value != DroneController.DroneInput.Yaw)
                {
                    DroneController.DroneInput.Yaw = Convert.ToSingle(value) * -1;
                    this.OnPropertyChanged("StickRotateValue");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la valeur du stick de déplacement en X
        /// </summary>
        public double StickXValue
        {
            get
            {
                return (DroneController.DroneInput.Roll);
            }
            set
            {
                if (value != DroneController.DroneInput.Roll)
                {
                    DroneController.DroneInput.Roll = Convert.ToSingle(value);
                    this.OnPropertyChanged("StickXValue");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit la valeur du stick de déplacement en Y
        /// </summary>
        public double StickYValue
        {
            get
            {
                return (DroneController.DroneInput.Pitch);
            }
            set
            {
                if (value != DroneController.DroneInput.Pitch)
                {
                    DroneController.DroneInput.Pitch = Convert.ToSingle(value);
                    this.OnPropertyChanged("StickYValue");
                }
            }
        }

        /// <summary>
        /// Obtient ou définit le brush pour l'affichage de la vidéo
        /// </summary>
        public Brush VideoBrush
        {
            get
            {
                return (this.m_oVideoBrush);
            }
            set
            {
                if (value != this.m_oVideoBrush)
                {
                    this.m_oVideoBrush = value;
                    this.OnPropertyChanged("VideoBrush");
                }
            }
        }

        /// <summary>
        /// Obtient la visibilité de la zone drone
        /// </summary>
        public Visibility VisibilityDrone
        {
            get
            {
                if (this.EmergencyMode)
                    return (Visibility.Collapsed);

                return (Visibility.Visible);
            }
        }

        /// <summary>
        /// Obtient la visibilité de la zone arrêt urgent
        /// </summary>
        public Visibility VisibilityEmergency
        {
            get
            {
                if (!this.EmergencyMode)
                    return (Visibility.Collapsed);

                return (Visibility.Visible);
            }
        }

        /// <summary>
        /// Obtient ou définit la visibilité de la zone de démarage
        /// </summary>
        public Visibility VisibilityStart
        {
            get
            {
                if (this.EmergencyMode)
                    return (Visibility.Collapsed);

                return (Visibility.Visible);
            }
        }

        #endregion

        #region Variables d'instances

        private int m_iAltitude;
        private int m_iBaterie;
        private bool m_bEmergencyMode;
        private bool m_bVideoIntegrationEnable;
        private Rect m_oClipBattery;

        private Brush m_oVideoBrush;
        private ZapVideoChannel m_eVideoChannel;

        private CommandViewModel m_oStart;
        private CommandViewModel m_oStop;
        private CommandViewModel m_oSwitch;
        private CommandViewModel m_oReset;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Constructeur statique de la classe
        /// </summary>
        static MainViewModel()
        {
            MainViewModel.Instance = new MainViewModel();
        }

        /// <summary>
        /// Constructeur de la classe
        /// </summary>
        public MainViewModel()
            : base()
        {
            // Initialisation

            this.m_iAltitude = 0;
            this.m_iBaterie = 0;
            this.m_oVideoBrush = null;
            this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_HORI;
            this.m_oClipBattery = new Rect(0, 0, 45, 20);

            this.m_bEmergencyMode = ConfigurationManager.AppSettings["StartEmergency"].Equals("1");
            this.m_bVideoIntegrationEnable = ConfigurationManager.AppSettings["VideoIntegrationEnable"].Equals("1");

            MainViewModel.DRONE_ACTIVATED = ConfigurationManager.AppSettings["DroneEnable"].Equals("1");

            // Gestion des commandes

            this.InitializeCommands();

            // On se branche sur le flux vidéo et on initialise le drone

            DroneController.VideoStream += new System.EventHandler<VideoStreamArgs>(this.VideoStreamTick);
            DroneController.NavigationData += new EventHandler<NavDataArgs>(this.NavigationData);

            if (MainViewModel.DRONE_ACTIVATED)
                DroneController.InitDrone();
        }

        #endregion

        #region Fonctions publiques

        /// <summary>
        /// Arrête le drone
        /// </summary>
        public void Shutdown()
        {
            DroneController.VideoStream -= new System.EventHandler<VideoStreamArgs>(this.VideoStreamTick);
            DroneController.NavigationData -= new EventHandler<NavDataArgs>(this.NavigationData);
            DroneController.Shutdown();
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Crée et initialise les commandes
        /// </summary>
        private void InitializeCommands()
        {
            this.m_oReset = new CommandViewModel();
            this.m_oReset.ExecuteAction = this.ResetDrone;

            this.m_oStart = new CommandViewModel();
            this.m_oStart.ExecuteAction = this.StartDrone;

            this.m_oStop = new CommandViewModel();
            this.m_oStop.ExecuteAction = this.StopDrone;

            this.m_oSwitch = new CommandViewModel();
            this.m_oSwitch.ExecuteAction = this.SwitchCamera;
        }

        /// <summary>
        /// Transforme l'altitude de mm vers m
        /// </summary>
        /// <param name="p_iAltitude">Altitude a convertir</param>
        /// <returns>Altitude en mètre</returns>
        private string MilimetersToMeters(double p_dAltitude)
        {
            double dMeter = Math.Floor(p_dAltitude / 1000);
            double dCentimeter = Math.Floor((p_dAltitude - (dMeter * 1000)) / 10);

            return (string.Format("{0} m {1} cm", dMeter, dCentimeter));
        }

        /// <summary>
        /// Se produit sur le Tick des informations de navigation
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void NavigationData(object sender, NavDataArgs e)
        {
            App.Current.Dispatcher.BeginInvoke(new Action(delegate
            {
                this.Altitude = e.NavData.altitude;
                this.Batterie = Convert.ToInt32(e.NavData.vbat_flying_percentage);
                this.EmergencyMode = NavDataArgs.EmergencyStatus == e.Status;
            }));
        }

        /// <summary>
        /// Se produit sur l'appuie du bouton reset
        /// </summary>
        /// <param name="p_sParam">Paramètre de l'action</param>
        private void ResetDrone(object p_sParam)
        {
            if (MainViewModel.DRONE_ACTIVATED)
                DroneController.Emergency();
        }

        /// <summary>
        /// Se produit sur l'appuie du bouton start
        /// </summary>
        /// <param name="p_sParam">Paramètre de l'action</param>
        private void StartDrone(object p_sParam)
        {
            if (MainViewModel.DRONE_ACTIVATED)
            {
                // On force le channel

                this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_HORI;
                DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_HORI);

                // On démare le drone

                DroneController.Launch();
            }
        }

        /// <summary>
        /// Se produit sur l'appuie du bouton stop
        /// </summary>
        /// <param name="p_sParam">Paramètre de l'action</param>
        private void StopDrone(object p_sParam)
        {
            if (MainViewModel.DRONE_ACTIVATED)
                DroneController.Land();
        }

        /// <summary>
        /// Se produit sur l'appuie du bouton switch caméra
        /// </summary>
        /// <param name="p_sParam">Paramètre de l'action</param>
        private void SwitchCamera(object p_sParam)
        {
            if (!MainViewModel.DRONE_ACTIVATED)
                return;

            // On switch la vidéo

            if (this.m_bVideoIntegrationEnable)
            {
                if (this.m_eVideoChannel == ZapVideoChannel.ZAP_CHANNEL_HORI)
                {
                    DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_VERT);
                    this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_VERT;
                }
                else if (this.m_eVideoChannel == ZapVideoChannel.ZAP_CHANNEL_VERT)
                {
                    DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_LARGE_HORI_SMALL_VERT);
                    this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_LARGE_HORI_SMALL_VERT;
                }
                else if (this.m_eVideoChannel == ZapVideoChannel.ZAP_CHANNEL_LARGE_HORI_SMALL_VERT)
                {
                    DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_LARGE_VERT_SMALL_HORI);
                    this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_LARGE_VERT_SMALL_HORI;
                }
                else
                {
                    DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_HORI);
                    this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_HORI;
                }
            }
            else
            {
                if (this.m_eVideoChannel == ZapVideoChannel.ZAP_CHANNEL_HORI)
                {
                    DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_VERT);
                    this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_VERT;
                }
                else
                {
                    DroneController.SwitchVideo(ZapVideoChannel.ZAP_CHANNEL_HORI);
                    this.m_eVideoChannel = ZapVideoChannel.ZAP_CHANNEL_HORI;
                }
            }
        }

        /// <summary>
        /// Est envoyé par le drone a chaque changement d'image vidéo
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void VideoStreamTick(object sender, VideoStreamArgs e)
        {
            App.Current.Dispatcher.BeginInvoke(new Action(delegate
            {
                MemoryStream oStream = new MemoryStream();
                e.Image.Save(oStream, ImageFormat.Bmp);
                oStream.Position = 0;

                BitmapImage image = new BitmapImage();
                image.BeginInit();
                image.StreamSource = oStream;
                image.EndInit();

                this.VideoBrush = new ImageBrush(image);
            }));
        }

        #endregion
    }
}
